import { Model } from "./Model";
import { M,R, get_table_fields, get_table_pk, array_columns } from "./Functions";
import * as _ from 'lodash';
import * as Q from 'q';
import {IServers, IRelationConfig} from '../interface/IFace'

/**
 * 关系设定类
 */
export class Relation{
    protected _one:Array<IRelationConfig>=[];
    protected _many:Array<IRelationConfig>=[];
    protected _extend:Array<IRelationConfig>=[];
    protected _table:string="";
    protected _pk:string="";
    protected _fields:string|Array<string>=[];
    protected _model:Model;
    protected _controller;
    protected _foreach:Array<Function>=[];
    public _config:IServers;

    /**
     *
     * @param Table 表名
     * @param Fields 字段列表
     * @param PK 主键
     */
    public constructor(Table,Fields:string|Array<string>="",PK="",config:IServers){
        this._config = config;
        this._table=Table;
        let _fields = [];
        if(_.isString(Fields)){
            _fields = Fields.split(',')
        }
        if(Fields.length==0||PK.length==0){
            _fields = get_table_fields(Table,this._config)
            PK = get_table_pk(Table,this._config)
        }
        this._fields=_fields;
        this._pk=PK;
        this._model = M(Table,this._config)
    }

    /**
     * 可以不通过构造函数来添加属性信息
     * @param Table
     * @param Fields
     * @param PK
     */
    public config(Table,Fields,PK){
        this._table=Table;
        this._fields=Fields;
        this._pk=PK;
        if(Table)
            this._model = M(Table,this._config);
    }
    /**
     * 拥有一个
     * @param name 关系名称
     * @param Table 表名称
     * @param Fields 要取的字段范围
     * @param TableField 子表字段
     * @param MainField 主表字段
     */
    public hasOne(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._one=Object.assign(this._one,{[config.name]:{
                name:'',
                table:'',
                fields:[],
                pk:'',
                fk:''
            }},{[config.name]:config})
        }
        return this;
    }
    get One(){
        return this._one;
    }
    set One(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._one=Object.assign(this._one,{[config.name]:{
                name:'',
                table:config.name,
                fields:[],
                pk:this._pk,
                fk:this._pk,
                    relation:false
            }},{[config.name]:config})
        }
    }
    /**
     * 有多个配置
     * @param config
     * @returns {boolean}
     */
    public hasMany(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._many=Object.assign(this._many,{[config.name]:{
                name:'',
                table:config.name,
                fields:[],
                pk:this._pk,
                fk:this._pk,
                relation:false
            }},{[config.name]:config})
        }
        return this;
    }
    get Many(){
        return this._many;
    }
    set Many(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._many=Object.assign(this._many,{[config.name]:{
                name:'',
                table:config.name,
                fields:[],
                pk:this._pk,
                fk:this._pk,
                    relation:false
            }},{[config.name]:config})
        }
    }

    /**
     * 扩展字段配置
     * @param config
     * @returns {boolean}
     */
    public extend(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._extend=Object.assign(this._extend,{[config.name]:{
                name:'',
                table:config.name,
                fields:[],
                pk:this._pk,
                fk:this._pk,
                    relation:false
            }},{[config.name]:config})
        }
        return this;
    }

    /**
     *
     * @returns {any}
     * @constructor
     */
    get Extend(){
        return this._extend;
    }

    /**
     *
     * @param config
     * @constructor
     */
    set Extend(config:any){
        if(_.isString(config.name)&&config.name.length>0){
            this._extend=Object.assign(this._extend,{[config.name]:{
                name:'',
                table:config.name,
                fields:[],
                pk:this._pk,
                fk:this._pk
            }},{[config.name]:config})
        }
    }
    // set Fields(fields){
    //     this._fields=fields;
    // }
    // get Fields(){
    //     return this._fields;
    // }
    /**
     * 获取对象
     * @param {Array<Number>} PKValues
     * @returns {any}
     */
    public async objects(PKValues:Array<Number>){
        if(_.isArray(PKValues)){
            let data = await this._model.fields(this._fields).where({
                [this._pk]:PKValues
            }).select()
            //开始循环属性配置并生成相关。。
            let Qs=[data];
            if(_.isArray(data)){
                // data = JSON.parse(JSON.stringify(data))
                for(let v of this._one){                    
                    if(!_.isString(v.relation)){
                        Qs.push(M(v.table,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }else{
                        Qs.push(R(v.relation,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }
                }
                for(let v of this._many){
                    if(!_.isString(v.relation)){
                        Qs.push(M(v.table,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }else{
                        Qs.push(R(v.relation,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }
                }
                for(let v of this._extend){
                    if(!_.isString(v.relation)){
                        Qs.push(M(v.table,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }else{
                        Qs.push(R(v.relation,this._config).fields(_.isFunction(v.fields)?v.fields():v.fields).where(v.where).where({[v.fk]:array_columns(data,v.pk,true)}).select())
                    }
                }
            }
            return Q.all(Qs).then(result=>{
                let i = 1,data=result[0],one={},many={},extend={},config={};
                for(let v of this._one){
                    one[v.name]={values:result[i],config:v};
                }
                for(let v of this._many){
                    many[v.name]={values:result[i],config:v};
                }
                for(let v of this._extend){
                    extend[v.name]={values:result[i],config:v};
                }
                _.forOwn(data,(v,k)=>{
                    _.forOwn(one,(d,f)=>{
                        data[k][f]=_.filter(d.values,{[d.config.fk]:data[k][d.config.pk]})[0]
                    })
                    _.forOwn(many,(d,f)=>{
                        data[k][f]=_.filter(d.values,{[d.config.fk]:data[k][d.config.pk]})
                    })
                    _.forOwn(extend,(d,f)=>{
                        data[k]=_.assign(v,_.filter(d.values,{[d.config.fk]:data[k][d.config.pk]})[0]);
                    })
                    for(let f of this._foreach){
                        if(f instanceof Function){
                            f(data[k])
                        }
                    }
                })
                return data;
            })
        }else{
            return Q("Error")
        }
    }
    protected data_columns(arr:any,column:any){
        let a =[];
        _.forOwn(arr,(v,k)=>{
            a.push(v.dataValues[column])
        })
        return _.uniq(a)
    }

    /**
     * 取出
     * @param arr
     * @returns {Array}
     */
    protected dataValues(arr,column:any=false){
        let a =[];
        _.forOwn(arr,(v,k)=>{
            if(false==column)
                a.push(v.dataValues)
            else
                a.push(v.dataValues[column])
        })
        return a
    }
    public where(where){
        this._model.where(where)
        return this;
    }

    public load(DbConfig:{}|string=null,) {
        var conf = {};
        if(_.isString(DbConfig)){
            //从定义目录加载
            conf = require(`${this._config.root}/Db/${DbConfig}`);
        }else if(_.isObject(DbConfig)){

        }

    }
    public fields(fields){
        this._fields=fields;
        return this;
    }
    public selectAndCount(){
        return this._model.fields([this._pk]).selectAndCount().then(d=>{
            if(d.rows.length==0){
                return [[],0];
            }
            var PKs = array_columns(d.rows,this._pk);
            return Q.all([
                this.objects.apply(this,[PKs]),
                d.count
            ]);
        })
    }
    public order(order){
        this._model.order(order)
        return this;
    }
    public save(data){
        return this._model.save(data).then(c=>{
            return c>-1;
        }).catch(e=>{
            return e;
        });
    }
    public page(p:number,n:number){
        this._model.page(p,n)
        return this;
    }
    public add(data) {
        return this._model.add(data).then(d=>{
            if(_.isObject(d)&&d[this._pk]>0){
                return this.objects.apply(this,[[d[this._pk]]]).then(p=>{
                    return p[0];
                })
            }else{
                return d;
            }
        })
    }
    // public find(){
    //     return this._model.find()
    // }
    public del(){
        return this._model.del();
    }
    public select(){
        return this._model.fields([this._pk]).select().then(d=>{
            if(_.isArray(d)&&d.length>0){
                var PKs = array_columns(d,this._pk);
                return this.objects.apply(this,[PKs]);
            }else{
                return [];
            }
        })
    }

    /**
     * 查询一个
     */
    public find(){
        return this._model.getFields(this._pk).then(d=>{
            if(_.isNumber(d)&&d>0){
                return this.objects([d]).then(data=>{
                    return _.isArray(data)&&data.length>0?data[0]:{}
                });
            }else{
                return {};
            }
        })
    }
}
exports.Relation = Relation;